# -*- coding: utf-8 -*-

from odoo import api, fields, models, tools, _
from odoo.exceptions import UserError, ValidationError,RedirectWarning
from odoo.addons.workflow.tools.profiler import profile as log_profile
import dateutil
import time
from datetime import timedelta
from odoo.tools.safe_eval import safe_eval
from odoo.addons.kthrp_dingding.interface import dingding_interface


class WorkflowApprove(models.TransientModel):
    _name = "workflow.approve"
    _description = "Kthrp Base Workflow Approve"

    # 意见
    comment = fields.Text(string="Note")
    # 审批人
    approver_ids = fields.Many2many(comodel_name="hr.employee", string="Approver",
                                    relation="wkf_action_approve_approver_hr_employee_rel",
                                    domain="[('id','in',role_approver_ids[0][2])]")

    # 通知
    notify_ids = fields.Many2many(comodel_name="hr.employee", string="Forward")
    # 附件
    attachment_ids = fields.Many2many(comodel_name='ir.attachment', string="Attachment")

    @api.model
    def default_get(self, fields):
        result = super(WorkflowApprove, self).default_get(fields)
        is_task = self.env.context.get('_is_task')
        model = self.env.context.get('active_model')
        active_id = self.env.context.get('active_id')
        task_obj = self.env['workflow.task'].suspend_security()
        if not self.env.user.related_employee_id:
            raise ValidationError(_('Invalid User'))
        current_employee = self.env.user.related_employee_id
        if is_task is True or model == 'workflow.task':
            task = task_obj.browse(active_id).filtered(lambda r: r.wkf_line_instance_state == 'processing')
            if not task:
                raise ValidationError(_('The current document approval status has been updated, please refresh.'))
        else:
            task = task_obj.search(
                [('allocated_approver_id', '=', current_employee.id), ('approve_result_id', '=', None),
                 ('wkf_line_instance_state', '=', 'processing'),
                 ('wkf_line_instance_id.instance_id.approve_document', '=', model + ',' + str(active_id))])
            if not task:
                raise ValidationError(_('The current document approval status has been updated, please refresh.'))
            else:
                task = task[0]
        if task.wkf_line_instance_id.workflow_line_id.is_check_node:
            result['comment'] = _('Check.')
        else:
            result['comment'] = _("Agree.")
        return result

    def _get_default_attachment_required(self):
        is_task = self.env.context.get('_is_task')
        model = self.env.context.get('active_model')
        active_id = self.env.context.get('active_id')
        task_obj = self.env['workflow.task'].suspend_security()
        if not self.env.user.related_employee_id:
            return False
        current_employee = self.env.user.related_employee_id
        if is_task is True or model == 'workflow.task':
            task = task_obj.browse(active_id).filtered(lambda r: r.wkf_line_instance_state == 'processing')
            if not task:
                return False
        else:
            task = task_obj.search(
                [('allocated_approver_id', '=', current_employee.id), ('approve_result_id', '=', None),
                 ('wkf_line_instance_state', '=', 'processing'),
                 ('wkf_line_instance_id.instance_id.approve_document', '=', model + ',' + str(active_id))])
            if not task:
                return False
            else:
                task = task[0]
        return task.wkf_line_instance_id.workflow_line_id.attachment_required

    attachment_required = fields.Boolean(default=_get_default_attachment_required)

    def _get_default_is_special_transfer(self):
        is_task = self.env.context.get('_is_task')
        model = self.env.context.get('active_model')
        active_id = self.env.context.get('active_id')
        task_obj = self.env['workflow.task'].suspend_security()
        if not self.env.user.related_employee_id:
            return False
        current_employee = self.env.user.related_employee_id
        if is_task is True or model == 'workflow.task':
            task = task_obj.browse(active_id).filtered(lambda r: r.wkf_line_instance_state == 'processing')
            if not task:
                return False
        else:
            task = task_obj.search(
                [('allocated_approver_id', '=', current_employee.id), ('approve_result_id', '=', None),
                 ('wkf_line_instance_state', '=', 'processing'),
                 ('wkf_line_instance_id.instance_id.approve_document', '=', model + ',' + str(active_id))])
            if not task:
                return False
            else:
                task = task[0]
        return task.is_special_transfer

    is_special_transfer = fields.Boolean(default=_get_default_is_special_transfer)

    @api.model
    def _default_role_approvers(self):
        is_task = self.env.context.get('_is_task')
        model = self.env.context.get('active_model')
        active_id = self.env.context.get('active_id')
        task_obj = self.env['workflow.task'].suspend_security()
        if not self.env.user.related_employee_id:
            return False
        current_employee = self.env.user.related_employee_id
        if is_task is True or model == 'workflow.task':
            task = task_obj.browse(active_id).filtered(lambda r: r.wkf_line_instance_state == 'processing')
            if not task:
                return False
        else:
            task = task_obj.search([('allocated_approver_id', '=', current_employee.id), ('approve_result_id', '=', None),
                                    ('wkf_line_instance_state', '=', 'processing'),
                                    ('wkf_line_instance_id.instance_id.approve_document', '=', model + ',' + str(active_id))])
            if not task:
                return False
            else:
                task = task[0]
        wkf_instance = task.wkf_line_instance_id.instance_id
        document_model = task.wkf_line_instance_id.instance_id.approve_document._name
        document_id = task.wkf_line_instance_id.instance_id.approve_document.id
        next_node = None
        transitions = task.wkf_line_instance_id.workflow_line_id.out_transition_ids.filtered(
            lambda t: t.to_workflow_line_id.node_type == "approve")
        transitions = sorted(transitions, key=lambda x: x.sequence)
        for rec in transitions:
            if not rec.wkf_condition_id:
                next_node = rec.to_workflow_line_id
                break
            else:
                if rec.wkf_condition_id.check_match_domain(document_model, document_id) is True:
                    next_node = rec.to_workflow_line_id
                    break
                else:
                    pass
        if next_node and len(next_node) != 1:
            return False
        elif not next_node:
            return False
        role_approver_ids = self.env['workflow.role'].get_role_approver(wkf_instance, next_node)
        if role_approver_ids and next_node.approver_type == 'designated_person':
            return role_approver_ids
        else:
            return []

    # 审批角色对应的审批人
    role_approver_ids = fields.Many2many(comodel_name="hr.employee", string="Approver", help="Approver",
                                         default=_default_role_approvers,
                                         compute="_compute_role_approvers",
                                         relation="wkf_action_approve_role_approver_hr_employee_rel")

    @api.model
    def _next_approver_type(self):
        is_task = self.env.context.get('_is_task')
        model = self.env.context.get('active_model')
        active_id = self.env.context.get('active_id')
        task_obj = self.env['workflow.task'].suspend_security()
        if not model:
            return False
        if not self.env.user.related_employee_id:
            return False
        current_employee = self.env.user.related_employee_id
        if is_task or model == 'workflow.task':
            task = task_obj.browse(active_id).filtered(lambda r: r.wkf_line_instance_state == 'processing')
            if not task:
                return False
        else:
            task = task_obj.search([('allocated_approver_id', '=', current_employee.id), ('approve_result_id', '=', None),
                                    ('wkf_line_instance_state', '=', 'processing'),
                                    ('wkf_line_instance_id.instance_id.approve_document', '=', model + ',' + str(active_id))])
            if not task:
                return False
            else:
                task = task[0]
        document_model = task.wkf_line_instance_id.instance_id.approve_document._name
        document_id = task.wkf_line_instance_id.instance_id.approve_document.id
        next_node = None
        transitions = task.wkf_line_instance_id.workflow_line_id.out_transition_ids.filtered(
            lambda t: t.to_workflow_line_id.node_type == "approve")
        transitions = sorted(transitions, key=lambda x: x.sequence)
        for rec in transitions:
            if not rec.wkf_condition_id:
                next_node = rec.to_workflow_line_id
                break
            else:
                if rec.wkf_condition_id.check_match_domain(document_model, document_id) is True:
                    next_node = rec.to_workflow_line_id
                    break
                else:
                    pass
        if next_node and len(next_node) != 1:
            return False
        elif not next_node:
            return False
        return next_node.approver_type

    # 下一审批节点审批人类型
    next_approver_type = fields.Char(string='Approver Type', readonly=True,
                                     default=_next_approver_type)

    @api.one
    def _compute_role_approvers(self):
        is_task = self.env.context.get('_is_task')
        model = self.env.context.get('active_model')
        active_id = self.env.context.get('active_id')
        task_obj = self.env['workflow.task'].suspend_security()
        if not self.env.user.related_employee_id:
            return False
        current_employee = self.env.user.related_employee_id
        if is_task is True or model == 'workflow.task':
            task = task_obj.browse(active_id).filtered(lambda r: r.wkf_line_instance_state == 'processing')
            if not task:
                return False
        else:
            task = task_obj.search(
                [('allocated_approver_id', '=', current_employee.id), ('approve_result_id', '=', None),
                 ('wkf_line_instance_state', '=', 'processing'),
                 ('wkf_line_instance_id.instance_id.approve_document', '=', model + ',' + str(active_id))])
            if not task:
                return False
            else:
                task = task[0]
        wkf_instance = task.wkf_line_instance_id.instance_id
        document_model = task.wkf_line_instance_id.instance_id.approve_document._name
        document_id = task.wkf_line_instance_id.instance_id.approve_document.id
        next_node = None
        transitions = task.wkf_line_instance_id.workflow_line_id.out_transition_ids.filtered(
            lambda t: t.to_workflow_line_id.node_type == "approve")
        transitions = sorted(transitions, key=lambda x: x.sequence)
        for rec in transitions:
            if not rec.wkf_condition_id:
                next_node = rec.to_workflow_line_id
                break
            else:
                if rec.wkf_condition_id.check_match_domain(document_model, document_id) is True:
                    next_node = rec.to_workflow_line_id
                    break
                else:
                    pass
        if next_node and len(next_node) != 1:
            return False
        elif not next_node:
            return False
        role_approver_ids = self.env['workflow.role'].get_role_approver(wkf_instance, next_node)
        if role_approver_ids and next_node.approver_type == 'designated_person':
            self.role_approver_ids = role_approver_ids.ids
        else:
            self.role_approver_ids = []

    def pre_confirm(self):
        self = self.suspend_security()
        is_task = self.env.context.get('_is_task')
        model = self.env.context.get('active_model')
        active_id = self.env.context.get('active_id')
        task_obj = self.env['workflow.task'].suspend_security()
        if not self.env.user.related_employee_id:
            return False
        current_employee = self.env.user.related_employee_id
        if is_task is True or model == 'workflow.task':
            task = task_obj.browse(active_id).filtered(lambda r: r.wkf_line_instance_state == 'processing')
            if not task:
                raise ValidationError(_('The current document approval status has been updated, please refresh.'))
        else:
            task = task_obj.search([('allocated_approver_id', '=', current_employee.id),
                                    ('approve_result_id', '=', False),
                                    ('wkf_line_instance_state', '=', 'processing'),
                                    ('wkf_line_instance_id.instance_id.approve_document', '=',
                                     model + ',' + str(active_id))])
            if not task:
                raise ValidationError(_('The current document approval status has been updated, please refresh.'))
            else:
                task = task[0]
        if task.approve_document._name == 'kthrp.expense.report':
            expense_report_id = task.approve_document
            expense_setting = self.env.ref('kthrp_expense.view_kthrp_expense_default_setting')
            if expense_report_id.type_id.offset_borrowing and expense_setting.approval_tips and not expense_report_id.reimbursement_borrowing_ids:
                borrowing_ids_a = self.env['kthrp.expense.borrowing'].search(
                    [('payee_id', '=', expense_report_id.reimbursement_employee_id.id),
                     ('state', '=', 'paid'), ('borrow_type_id.type', '=', 'borrowing'),
                     ('balance_amount', '>', 0)])
                reimbursement_borrowing_list = []
                expense_report_objs = self.env['kthrp.expense.report'].search(
                    [('state', 'not in', ['cancelled', 'paid', 'part_paid'])])
                for expense_report_obj in expense_report_objs:
                    reimbursement_borrowing_list += expense_report_obj.reimbursement_borrowing_ids.mapped(
                        'expense_borrowing_id').ids
                borrowing_ids_b = self.env['kthrp.expense.borrowing'].browse(reimbursement_borrowing_list)
                if borrowing_ids_a - borrowing_ids_b:
                    return _('The reimbursement[%s] have unreturned borrowing, do you approve the document after choose it?') % expense_report_id.doc_number

    @api.multi
    def action_approve(self):
        self = self.suspend_security()
        is_task = self.env.context.get('_is_task')
        model = self.env.context.get('active_model')
        active_id = self.env.context.get('active_id')
        task_obj = self.env['workflow.task'].suspend_security()
        if not self.env.user.related_employee_id:
            return False
        current_employee = self.env.user.related_employee_id
        if is_task is True or model == 'workflow.task':
            task = task_obj.browse(active_id).filtered(lambda r: r.wkf_line_instance_state == 'processing')
            if not task:
                raise ValidationError(_('The current document approval status has been updated, please refresh.'))
        else:
            task = task_obj.search([('allocated_approver_id', '=', current_employee.id),
                                    ('approve_result_id', '=', False),
                                    ('wkf_line_instance_state', '=', 'processing'),
                                    ('wkf_line_instance_id.instance_id.approve_document', '=', model + ',' + str(active_id))])
            if not task:
                raise ValidationError(_('The current document approval status has been updated, please refresh.'))
            else:
                task = task[0]
        task.with_context(prefetch_fields=False).wkf_line_instance_id.with_context(prefetch_fields=False).instance_id.with_context(prefetch_fields=False).approve_document.set_state_approving()
        designated_approver = self.approver_ids.ids
        if task.is_special_transfer:
            designated_approver += task.from_task_id.allocated_approver_id.ids
        if self.next_approver_type == 'designated_person' and not designated_approver:
            raise ValidationError(
                _("Document need be designated approver."))
        task.with_context({'designated_approver': self.approver_ids.ids, 'lang': self.env.context.get('lang', 'en_US')}).action_approve()
        task.approve_note = self.comment
        task.approve_attachments_ids = self.attachment_ids
        task.transfer_approve_attachments()
        task.transfer_approve_attachments_x()
        task.notify_ids = self.notify_ids
        if self.notify_ids:
            task.with_context({'partner_ids': self.notify_ids, 'lang':self.env.context.get('lang','en_US')})._wkf_send_email('notify')
        # 勾选会签后，仅最后一个节点完成后才发送通知
        if not (task.wkf_line_instance_id.workflow_line_id.countersign and task.wkf_line_instance_id.state == 'processing'):
            task.transition_notify()
        task.set_workflow_message_done(task.approve_document, 'approve')
        setting = self.env.ref('workflow.workflow_default_setting', raise_if_not_found=False)

        # 更新 oa 消息状态
        if self.has_install_module('kthrp_dingding'):
            ding_config_obj = self.env['kthrp.dingding.config']
            # 直发直接更新 oa 消息状态
            if ding_config_obj.search([], limit=1).send_type == 'hrp_direct_send':
                # 更新钉钉 oa 消息状态
                if task.dingding_msg_oa:
                    Dingtalk = dingding_interface.KthrpDingtalk
                    oa_task_id, ding_config_id = task.dingding_msg_oa.split('-')[0], task.dingding_msg_oa.split('-')[1]
                    ding_config_obj = self.env['kthrp.dingding.config']
                    corpid, corpsecret, agent_id, token_dict = ding_config_obj.browse(int(ding_config_id)).get_ding_common_message()
                    ding_obj = Dingtalk(corpid, corpsecret, agent_id, token_dict)
                    ding_obj.update_oa_msg_state(int(oa_task_id))

                    # 同一节点多个人审批，一人通过，则将其他消息状态同步更新
                    if len(task.wkf_line_instance_id.task_ids) > 1:
                        if task.wkf_line_instance_state == 'processed':
                            for tk in task.wkf_line_instance_id.task_ids:
                                if tk.dingding_msg_oa:
                                    oa_task_id, ding_config_id = tk.dingding_msg_oa.split('-')[0], tk.dingding_msg_oa.split('-')[1]
                                    ding_config_obj = self.env['kthrp.dingding.config']
                                    corpid, corpsecret, agent_id, token_dict = ding_config_obj.browse(int(ding_config_id)).get_ding_common_message()
                                    ding_obj = Dingtalk(corpid, corpsecret, agent_id, token_dict)
                                    ding_obj.update_oa_msg_state(int(oa_task_id))
            # 前置机发送需要找到对应消息的 后台消息发送列表中的 update_oa_flag 值
            else:
                update_task_ids = [task.id, -1]
                # 同一节点多个人审批，一人通过，则将其他消息状态同步更新
                if len(task.wkf_line_instance_id.task_ids) > 1:
                    if task.wkf_line_instance_state == 'processed':
                        update_task_ids += task.wkf_line_instance_id.task_ids.ids
                update_sql = "update kthrp_dingding_msg_to_be_sent set update_oa_flag = 't' where workflow_task_id in {};".format(tuple(set(update_task_ids)))

                self._cr.execute(update_sql)

        if not setting.automatic_skip_after_approval:
            return
        return setting.after_process_task_return()
        # return_action = self.env.ref('workflow.action_workflow_task_to_be_approved')
        # act = return_action.read(['name', 'type', 'res_model', 'view_type', 'view_mode', 'target', 'domain', 'context','view_id','search_view_id'])[0]
        # if act.get('context') and  act.get('context') != 'False':
        #     context = eval(act['context'])
        #     context['clear_breadcrumbs']= True
        #     context['search_disable_custom_filters']= False
        #     context['workflow_approving'] = True
        #     context = str(context).decode('gbk')
        #     act['context'] = context
        # else:
        #     act['context'] = ("{'clear_breadcrumbs':True,'workflow_approving':True}").decode('gbk')
        # act['domain'] = [('allocated_user_id', '=', self.env.user.id)]
        # return act

    # 移动端字段与功能
    mobile_operation = fields.Char()
    mobile_task_id = fields.Integer()

    @api.multi
    def mobile_approve(self, task_id, comment, designated_approvers=None):
        self = self.suspend_security()
        task_obj = self.env['workflow.task'].suspend_security()
        task = task_obj.browse(task_id)
        ctx = {'active_model': 'workflow.task', '_is_task': True, 'active_id': task_id,
               'lang': self.env.context.get('lang', 'en_US')}
        if task.wkf_line_instance_id.workflow_line_id.is_check_node:
            default_comment = _('Check.')
        else:
            default_comment = _("Agree.")
        record = self.with_context(ctx).create({'comment': comment or default_comment,
                                                'mobile_operation': 'approve', 'mobile_task_id': task_id})
        record.approver_ids = designated_approvers.ids
        try:
            record.sudo(task.allocated_user_id.id).with_context(ctx).action_approve()
            result = (True, 'ok')
        except Exception as e:
            if hasattr(e, 'name'):
                error_msg = e.name
            elif hasattr(e, 'args'):
                error_msg = e.args[0]
            else:
                error_msg = str(e)
            result = (False, error_msg)
            self.env.cr.rollback()
        return result


class WorkflowReject(models.TransientModel):
    _name = "workflow.reject"
    _description = "Kthrp Base Workflow Reject"

    # 意见
    comment = fields.Text(string="Note", default="", required=True)
    # 通知
    notify_ids = fields.Many2many(comodel_name="hr.employee", string="Forward")
    # 附件
    attachment_ids = fields.Many2many(comodel_name='ir.attachment', string="Attachment")

    def _get_default_return_back(self):
        setting = self.env.ref('workflow.workflow_default_setting', raise_if_not_found=False)
        if setting:
            return setting.default_of_skip_approved_node
        else:
            return False

    #
    return_back = fields.Boolean(string='Skip Approved Nodes When Submitting', default=_get_default_return_back)

    @api.model
    def default_get(self, fields):
        result = super(WorkflowReject, self).default_get(fields)
        is_task = self.env.context.get('_is_task')
        model = self.env.context.get('active_model')
        active_id = self.env.context.get('active_id')
        task_obj = self.env['workflow.task'].suspend_security()
        current_employee = self.env.user.related_employee_id
        if is_task or model == 'workflow.task':
            task = task_obj.browse(active_id).filtered(lambda r: r.wkf_line_instance_state == 'processing')
            if not task:
                raise ValidationError(_('The current document approval status has been updated, please refresh.'))
        else:
            task = task_obj.search([('allocated_approver_id', '=', current_employee.id),
                                    ('approve_result_id', '=', None),
                                    ('wkf_line_instance_state', '=', 'processing'),
                                    ('wkf_line_instance_id.instance_id.approve_document', '=', model+','+str(active_id))])
            if not task:
                raise ValidationError(_('The current document approval status has been updated, please refresh.'))
        return result

    @api.multi
    def action_reject(self):
        self = self.suspend_security()
        is_task = self.env.context.get('_is_task')
        model = self.env.context.get('active_model')
        active_id = self.env.context.get('active_id')
        task_obj = self.env['workflow.task'].suspend_security()
        current_employee = self.env.user.related_employee_id
        if is_task or model == 'workflow.task':
            task = task_obj.browse(active_id).filtered(lambda r: r.wkf_line_instance_state == 'processing')
            if not task:
                raise ValidationError(_('The current document approval status has been updated, please refresh.'))
        else:
            task = task_obj.search([('allocated_approver_id', '=', current_employee.id),
                                    ('approve_result_id', '=', None),
                                    ('wkf_line_instance_state', '=', 'processing'),
                                    ('wkf_line_instance_id.instance_id.approve_document', '=', model+','+str(active_id))])
            if not task:
                raise ValidationError(_('The current document approval status has been updated, please refresh.'))
            else:
                task = task[0]
        task.action_refuse()
        task.approve_note = self.comment
        task.wkf_line_instance_id.instance_id.approve_document.set_state_rejected()
        task.approve_attachments_ids = self.attachment_ids
        task.transfer_approve_attachments()
        task.notify_ids = self.notify_ids
        task._wkf_send_email('rejected')
        task.set_workflow_message_done(task.approve_document, 'approve')
        if self.return_back:
            task.approve_document.is_wkf_return = True
        else:
            task.approve_document.is_wkf_return = False
        if self.notify_ids:
            task.with_context({'partner_ids': self.notify_ids, 'lang':self.env.context.get('lang','en_US')})._wkf_send_email('notify')
        setting = self.env.ref('workflow.workflow_default_setting', raise_if_not_found=False)

        # 更新 oa 消息状态
        if self.has_install_module('kthrp_dingding'):
            ding_config_obj = self.env['kthrp.dingding.config']
            # 直发直接更新 oa 消息状态
            if ding_config_obj.search([], limit=1).send_type == 'hrp_direct_send':
                # 更新钉钉 oa 消息状态
                if task.dingding_msg_oa:
                    Dingtalk = dingding_interface.KthrpDingtalk
                    oa_task_id, ding_config_id = task.dingding_msg_oa.split('-')[0], task.dingding_msg_oa.split('-')[1]
                    ding_config_obj = self.env['kthrp.dingding.config']
                    corpid, corpsecret, agent_id, token_dict = ding_config_obj.browse(
                        int(ding_config_id)).get_ding_common_message()
                    ding_obj = Dingtalk(corpid, corpsecret, agent_id, token_dict)
                    ding_obj.update_oa_msg_state(int(oa_task_id))

                    # 同一节点多个人审批，一人通过，则将其他消息状态同步更新
                    if len(task.wkf_line_instance_id.task_ids) > 1:
                        if task.wkf_line_instance_state == 'processed':
                            for tk in task.wkf_line_instance_id.task_ids:
                                if tk.dingding_msg_oa:
                                    oa_task_id, ding_config_id = tk.dingding_msg_oa.split('-')[0], tk.dingding_msg_oa.split('-')[1]
                                    ding_config_obj = self.env['kthrp.dingding.config']
                                    corpid, corpsecret, agent_id, token_dict = ding_config_obj.browse(int(ding_config_id)).get_ding_common_message()
                                    ding_obj = Dingtalk(corpid, corpsecret, agent_id, token_dict)
                                    ding_obj.update_oa_msg_state(int(oa_task_id))
            # 前置机发送需要找到对应消息的 后台消息发送列表中的 update_oa_flag 值
            else:
                update_task_ids = [task.id, -1]
                # 同一节点多个人审批，一人通过，则将其他消息状态同步更新
                if len(task.wkf_line_instance_id.task_ids) > 1:
                    if task.wkf_line_instance_state == 'processed':
                        update_task_ids += task.wkf_line_instance_id.task_ids.ids
                update_sql = "update kthrp_dingding_msg_to_be_sent set update_oa_flag = 't' where workflow_task_id in {};".format(
                    tuple(set(update_task_ids)))

                self._cr.execute(update_sql)

        if not setting.automatic_skip_after_approval:
            return
        return setting.after_process_task_return()
        # return_action = self.env.ref('workflow.action_workflow_task_to_be_approved')
        # act = return_action.read(['name', 'type', 'res_model', 'view_type', 'view_mode', 'target', 'domain', 'context'])[0]
        # if act.get('context') and act.get('context') != 'False':
        #     context = eval(act['context'])
        #     context['clear_breadcrumbs']= True
        #     context['search_disable_custom_filters']= False
        #     context['workflow_approving'] = True
        #     context = str(context).decode('gbk')
        #     act['context'] = context
        # else:
        #     act['context'] = ("{'clear_breadcrumbs':True,'workflow_approving':True}").decode('gbk')
        # act['domain'] = [('allocated_user_id', '=', self.env.user.id)]
        # return act
    # 移动端字段与功能
    mobile_operation = fields.Char()
    mobile_task_id = fields.Integer()

    @api.multi
    def mobile_reject(self, task_id, comment, skip_approved_node):
        self = self.suspend_security()
        ctx = {'active_model': 'workflow.task', '_is_task': True, 'active_id': task_id,
               'lang': self.env.context.get('lang', 'en_US')}
        record = self.with_context(ctx).create({'comment': comment or _("Refuse."), 'return_back': self.process_boolean(skip_approved_node)})
        task_obj = self.env['workflow.task'].suspend_security()
        task = task_obj.browse(task_id)
        try:
            record.sudo(task.allocated_user_id.id).with_context(ctx).action_reject()
            result = (True, 'ok')
        except Exception as e:
            if hasattr(e, 'name'):
                error_msg = e.name
            elif hasattr(e, 'args'):
                error_msg = e.args[0]
            else:
                error_msg = str(e)
            result = (False, error_msg)
            self.env.cr.rollback()
        return result

    @api.model
    def process_boolean(self, string):
        if string == 'true':
            return True
        else:
            return False


class WorkflowReturn(models.TransientModel):
    _name = "workflow.return"
    _description = "Kthrp Base Workflow Return"

    # 退回至节点
    back_to_node_id = fields.Many2one(comodel_name="workflow.line", string="Return To",
                                      domain="[('id','in',node_ids[0][2])]")
    # 意见
    comment = fields.Text(string="Note")
    # 通知
    notify_ids = fields.Many2many(comodel_name="hr.employee", string="Forward")
    # 附件
    attachment_ids = fields.Many2many(comodel_name='ir.attachment', string="Attachment")

    @api.model
    def default_get(self, fields):
        result = super(WorkflowReturn, self).default_get(fields)
        is_task = self.env.context.get('_is_task')
        model = self.env.context.get('active_model')
        active_id = self.env.context.get('active_id')
        task_obj = self.env['workflow.task'].suspend_security()
        if is_task is True or model == 'workflow.task':
            current_task = task_obj.browse(active_id).filtered(lambda r: r.wkf_line_instance_state == 'processing')
            if not current_task:
                raise ValidationError(_('The current document approval status has been updated, please refresh.'))
        else:
            current_task = task_obj.search(
                [('allocated_approver_id', '=', self.env.user.related_employee_id.id), ('approve_result_id', '=', None),
                 ('wkf_line_instance_state', '=', 'processing'),
                 ('wkf_line_instance_id.instance_id.approve_document', '=', model + ',' + str(active_id))])
            if not current_task:
                raise ValidationError(_('The current document approval status has been updated, please refresh.'))
        return result

    @api.model
    def _default_nodes(self):
        is_task = self.env.context.get('_is_task')
        model = self.env.context.get('active_model')
        active_id = self.env.context.get('active_id')
        task_obj = self.env['workflow.task'].suspend_security()
        if is_task:
            task = task_obj.browse(active_id)
            approve_document = task.wkf_line_instance_id.instance_id.approve_document
            return self.env['workflow.instance.line'].search([('instance_id.approve_document', '=', approve_document._name+','+str(approve_document.id)),
                                                                         ('instance_id.state', '=', 'processing'),
                                                                         ('state', '=', 'processed'),
                                                                         ('workflow_line_id.node_type', '=', 'approve')]).mapped('workflow_line_id')
        else:
            return self.env['workflow.instance.line'].search([('instance_id.approve_document', '=', model+','+str(active_id)),
                                                                         ('instance_id.state', '=', 'processing'),
                                                                         ('state', '=', 'processed'),
                                                                         ('workflow_line_id.node_type', '=', 'approve')]).mapped('workflow_line_id')

    # 对应实例的节点
    node_ids = fields.Many2many(comodel_name="workflow.line", default=_default_nodes, compute="_compute_nodes")

    @api.one
    def _compute_nodes(self):
        is_task = self.env.context.get('_is_task')
        model = self.env.context.get('active_model')
        active_id = self.env.context.get('active_id')
        task_obj = self.env['workflow.task'].suspend_security()
        if is_task:
            task = task_obj.browse(active_id)
            approve_document = task.wkf_line_instance_id.instance_id.approve_document
            nodes = self.env['workflow.instance.line'].search([('instance_id.approve_document', '=', approve_document._name+','+str(approve_document.id)),
                                                                         ('instance_id.state', '=', 'processing'),
                                                                         ('state', '=', 'processed'),
                                                                         ('workflow_line_id.node_type', '=', 'approve')]).mapped('workflow_line_id')
            self.node_ids = nodes.ids
        else:
            nodes = self.env['workflow.instance.line'].search([('instance_id.approve_document', '=', model+','+str(active_id)),
                                                                         ('instance_id.state', '=', 'processing'),
                                                                         ('state', '=', 'processed'),
                                                                         ('workflow_line_id.node_type', '=', 'approve')]).mapped('workflow_line_id')
            self.node_ids = nodes.ids

    def get_task(self, task):
        pass

    # 回退操作
    @api.multi
    def action_return(self):
        self = self.suspend_security()
        is_task = self.env.context.get('_is_task')
        model = self.env.context.get('active_model')
        active_id = self.env.context.get('active_id')
        task_obj = self.env['workflow.task'].suspend_security()
        if is_task is True or model == 'workflow.task':
            current_task = task_obj.browse(active_id).filtered(lambda r: r.wkf_line_instance_state == 'processing')
            if not current_task:
                raise ValidationError(_('The current document approval status has been updated, please refresh.'))
        else:
            current_task = task_obj.search(
                [('allocated_approver_id', '=', self.env.user.related_employee_id.id), ('approve_result_id', '=', None),
                 ('wkf_line_instance_state', '=', 'processing'),
                 ('wkf_line_instance_id.instance_id.approve_document', '=', model + ',' + str(active_id))])
            if not current_task:
                raise ValidationError(_('The current document approval status has been updated, please refresh.'))
            else:
                current_task = current_task[0]
        last_node = current_task.wkf_line_instance_id
        if current_task:
            current_task.approve_note = self.comment
            current_task.approve_attachments_ids = self.attachment_ids
            current_task.notify_ids = self.notify_ids
            current_task.approve_time = fields.Datetime.now()
            current_task.actual_approver_id = self.env.user.related_employee_id.id
            current_task.signature_image = self.env.user.related_employee_id.signature_image
            current_task.transfer_approve_attachments()
        self.get_task(current_task)
        back_to_node_instance = last_node.instance_id.instance_line_ids.filtered(lambda line: line.workflow_line_id.id == self.back_to_node_id.id).sorted(key=lambda r: r.create_date, reverse=True)[0]
        self._fallback_to_node(last_node, current_task)
        # self.remove_duplicate_task(self.back_to_node)
        back_to_node_instance.state = 'processing'
        fallback_approver_ids = []
        # 新增待审批task
        for rec in back_to_node_instance.task_ids:
            if rec.actual_approver_id:
                fallback_approver_ids.append(rec.actual_approver_id.id)
        approvers_ids,auto_approve = back_to_node_instance.instance_id.with_context({'designated_approver': fallback_approver_ids}).get_approve_ids(
            back_to_node_instance.in_transition_ids[0].from_instance_line_id, back_to_node_instance.workflow_line_id)
        task_obj = self.env['workflow.task'].suspend_security()
        for approver_list in approvers_ids:
            substitution_id = back_to_node_instance.workflow_line_id.check_substitution_rule(back_to_node_instance.instance_id, approver_list[0])
            new_task_vals = {
                'allocated_approver_id': substitution_id or approver_list[0],
                'wkf_line_instance_id': back_to_node_instance.id,
                'last_approver_id': self.env.user.related_employee_id.id,
                'last_wkf_line_instance_id': last_node.id,
                'last_approve_time': last_node.process_time,
                'authority_transfer': approver_list[1],
                'auto_approve': auto_approve
            }
            if approver_list[1]:
                new_task_vals['authority_transfer'] = approver_list[1]
            task_obj.create(new_task_vals)
        self.remove_duplicate_task(back_to_node_instance)
        for task in back_to_node_instance.task_ids:
            task._wkf_send_email('need_approval')
        if self.notify_ids:
            back_to_node_instance.task_ids[0].notify_ids = self.notify_ids
            back_to_node_instance.task_ids[0].with_context({'partner_ids': self.notify_ids, 'lang':self.env.context.get('lang','en_US')})._wkf_send_email('notify')


    # 去掉分配的审批人重复的task
    @api.model
    def remove_duplicate_task(self, node_instance=None):
        if node_instance:
            allocated_approver_list = []
            for task in [task for task in node_instance.task_ids if not task.approve_result_id]:
                if task.allocated_approver_id not in allocated_approver_list:
                    allocated_approver_list.append(task.allocated_approver_id)
                else:
                    task.unlink()

    # 将实例回退到指定节点
    @api.model
    def _fallback_to_node(self, node, current_task):
        node.state = 'returned'
        node.process_time = fields.Datetime.now()
        signal_obj = self.env['workflow.signal']
        return_signal = signal_obj.search([('code', '=', 'return')], limit=1)
        for task in node.task_ids.filtered(lambda x: x.id != current_task.id and not x.approve_result_id):
            task.unlink()
        current_task.approve_result_id = return_signal.id
        return

    # 移动端字段与功能
    mobile_operation = fields.Char()
    mobile_task_id = fields.Integer()

    @api.multi
    def mobile_return(self, task_id, comment, back_to):
        interface_obj = self.env['map.interface']
        task_obj = self.env['workflow.task'].suspend_security()
        task = task_obj.browse(task_id)
        user = task.allocated_user_id
        self = self.sudo(user.id)
        self = self.suspend_security()
        ctx = {'active_model': 'workflow.task', '_is_task': True, 'active_id': task_id,
               'lang': self.env.context.get('lang', 'en_US')}
        vals = {'comment': comment or '', 'back_to_node_id': back_to.id}
        record = interface_obj.find_or_create_wizard(user, self._name, vals, ctx, 'return')
        if record.comment != comment:
            record.comment = comment
        try:
            record.sudo(task.allocated_user_id.id).with_context(ctx).action_return()
            result = (True, 'ok')
        except Exception as e:
            if hasattr(e, 'name'):
                error_msg = e.name
            elif hasattr(e, 'args'):
                error_msg = e.args[0]
            else:
                error_msg = str(e)
            result = (False, error_msg)
            self.env.cr.rollback()
        return result


class WorkflowTransfer(models.TransientModel):
    _name = "workflow.transfer"
    _description = "Kthrp Base Workflow Transfer"

    # 转交至
    transfer_to_id = fields.Many2one(comodel_name="hr.employee", string="Transfer To", required=True)
    # 意见
    comment = fields.Text(string="Note")
    # 通知
    notify_ids = fields.Many2many(comodel_name="hr.employee", string="Forward")
    # 附件
    attachment_ids = fields.Many2many(comodel_name='ir.attachment', string="Attachment")

    def get_default_after_return(self):
        if self.has_install_module('kthrp_chanyi_ext'):
            return False
        setting = self.env.ref('workflow.workflow_default_setting', raise_if_not_found=False)
        if setting:
            return setting.default_of_return
        else:
            return True

    # 审批后返回
    after_return = fields.Boolean(string='Return', default=get_default_after_return,
                                  help='If checked, you need to confirm after being approved by the person transferred to.')

    @api.model
    def default_get(self, fields):
        result = super(WorkflowTransfer, self).default_get(fields)
        is_task = self.env.context.get('_is_task')
        model = self.env.context.get('active_model')
        active_id = self.env.context.get('active_id')
        task_obj = self.env['workflow.task'].suspend_security()
        current_employee = self.env.user.related_employee_id
        if self.transfer_to_id.id == current_employee.id:
            raise ValidationError(_('Invalid Transfer To'))
        if is_task or model == 'workflow.task':
            task = task_obj.browse(active_id).filtered(lambda r: r.wkf_line_instance_state == 'processing')
            if not task:
                raise ValidationError(_('The current document approval status has been updated, please refresh.'))
        else:
            task = task_obj.search([('allocated_approver_id', '=', current_employee.id),
                                    ('approve_result_id', '=', None),
                                    ('wkf_line_instance_state', '=', 'processing'),
                                    ('wkf_line_instance_id.instance_id.approve_document', '=', model+','+str(active_id))])
            if not task:
                raise ValidationError(_('The current document approval status has been updated, please refresh.'))
        return result

    @api.multi
    def get_transfer_text(self, a, b):
        if not self.after_return:
            result = _('Transfer from %s to %s') % (a.name, b.name)
        else:
            result = _('%s consult from %s') % (a.name, b.name)
        return result


    def get_task(self, task):
        pass

    def action_transfer_extra_process(self, task_obj, model, active_id, current_employee):
        pass

    @api.multi
    def action_transfer(self):
        self = self.suspend_security()
        is_task = self.env.context.get('_is_task')
        model = self.env.context.get('active_model')
        active_id = self.env.context.get('active_id')
        task_obj = self.env['workflow.task'].suspend_security()
        current_employee = self.env.user.related_employee_id
        if self.transfer_to_id.id == current_employee.id:
            return
        if is_task or model == 'workflow.task':
            task = task_obj.browse(active_id).filtered(lambda r: r.wkf_line_instance_state == 'processing')
            if not task:
                raise ValidationError(_('The current document approval status has been updated, please refresh.'))
            self.action_transfer_extra_process(task_obj, model, active_id, current_employee)
            trans_task = task_obj.create({'allocated_approver_id': self.transfer_to_id.id,
                                          'wkf_line_instance_id': task.wkf_line_instance_id.id,
                                          'authority_transfer': self.get_transfer_text(current_employee, self.transfer_to_id)
                                          })
        else:
            task = task_obj.search([('allocated_approver_id', '=', current_employee.id),
                                    ('approve_result_id', '=', None),
                                    ('wkf_line_instance_state', '=', 'processing'),
                                    ('wkf_line_instance_id.instance_id.approve_document', '=', model+','+str(active_id))])
            if not task:
                raise ValidationError(_('The current document approval status has been updated, please refresh.'))
            else:
                task = task[0]
            trans_task = task_obj.search([('allocated_approver_id', '=', self.transfer_to_id.id),
                                          ('approve_result_id', '=', None),
                                          ('wkf_line_instance_state', '=', 'processing'),
                                          ('wkf_line_instance_id.instance_id.approve_document', '=', model+','+str(active_id))])
            if len(trans_task) == 0:
                self.action_transfer_extra_process(task_obj, model, active_id, current_employee)
                trans_task = task_obj.create({'allocated_approver_id': self.transfer_to_id.id,
                                              'wkf_line_instance_id': task.wkf_line_instance_id.id,
                                              'authority_transfer': self.get_transfer_text(current_employee,
                                                                                           self.transfer_to_id)})
        if self.after_return:
            trans_task.write({'is_special_transfer': True, 'from_task_id': task.id, 'authority_transfer': _('Transfer Nodes')})
        else:
            trans_task.write({'is_special_transfer': False, 'from_task_id': task.id})
        signal_id = self.env['workflow.signal'].search([('code', '=', 'transfer')], limit=1)
        # 审批节点没有配置转交信号，管理员仍可以强行转交
        task.approve_attachments_ids = self.attachment_ids
        task.notify_ids = self.notify_ids
        task.write({
            'actual_approver_id': current_employee.id,
            'signature_image': current_employee.signature_image,
            'approve_result_id': signal_id.id,
            'approve_note': self.comment,
            'approve_time': fields.Datetime.now(),
            'transfer_to_id': self.transfer_to_id.id
        })
        task.transfer_approve_attachments()
        task.with_context(prefetch_fields=False).wkf_line_instance_id.with_context(prefetch_fields=False).instance_id.with_context(prefetch_fields=False).approve_document.set_state_approving()
        if self.after_return:
            trans_task._wkf_send_email('need_approval_consult')
        else:
            trans_task._wkf_send_email('need_approval_transfer')
        if self.notify_ids:
            task.with_context({'partner_ids': self.notify_ids,'lang':self.env.context.get('lang','en_US')})._wkf_send_email('notify')
        setting = self.env.ref('workflow.workflow_default_setting', raise_if_not_found=False)
        # 获取task
        self.get_task(task)
        if not setting.automatic_skip_after_approval:
            return
        return setting.after_process_task_return()
        # return_action = self.env.ref('workflow.action_workflow_task_to_be_approved')
        # act = return_action.read(['name', 'type', 'res_model', 'view_type', 'view_mode', 'target', 'domain', 'context'])[0]
        # if act.get('context') and act.get('context') != 'False':
        #     context = eval(act['context'])
        #     context['clear_breadcrumbs']= True
        #     context['search_disable_custom_filters']= False
        #     context['workflow_approving'] = True
        #     context = str(context).decode('gbk')
        #     act['context'] = context
        # else:
        #     act['context'] = ("{'clear_breadcrumbs':True,'workflow_approving':True}").decode('gbk')
        # act['domain'] = [('allocated_user_id', '=', self.env.user.id)]
        # return act
    # 移动端字段与功能
    mobile_operation = fields.Char()
    mobile_task_id = fields.Integer()

    @api.multi
    def mobile_transfer(self, task_id, comment, after_return, employee):
        self = self.suspend_security()
        after_return = self.process_boolean(after_return)
        ctx = {'active_model': 'workflow.task', '_is_task': True, 'active_id': task_id,
               'lang': self.env.context.get('lang', 'en_US')}
        record = self.with_context(ctx).create({'comment': comment or _("Transfer."), 'after_return': after_return,
                                                'transfer_to_id': employee.id})
        task_obj = self.env['workflow.task'].suspend_security()
        task = task_obj.browse(task_id)
        try:
            record.sudo(task.allocated_user_id.id).with_context(ctx).action_transfer()
            result = (True, 'ok')
        except Exception as e:
            if hasattr(e, 'name'):
                error_msg = e.name
            elif hasattr(e, 'args'):
                error_msg = e.args[0]
            else:
                error_msg = str(e)
            result = (False, error_msg)
            self.env.cr.rollback()
        return result

    @api.model
    def process_boolean(self, string):
        if string == 'true':
            return True
        else:
            return False


class WorkflowLinkup(models.TransientModel):
    _name = "workflow.linkup"
    _description = "Kthrp Base Workflow Linkup"

    # 转交至
    transfer_to_ids = fields.Many2many(comodel_name="hr.employee", string="Transfer To", required=True)
    # 意见
    comment = fields.Text(string="Note")
    # 通知
    notify_ids = fields.Many2many(comodel_name="hr.employee", string="Forward")
    # 附件
    attachment_ids = fields.Many2many(comodel_name='ir.attachment', string="Attachment")



    @api.model
    def default_get(self, fields):
        result = super(WorkflowLinkup, self).default_get(fields)
        is_task = self.env.context.get('_is_task')
        model = self.env.context.get('active_model')
        active_id = self.env.context.get('active_id')
        task_obj = self.env['workflow.task'].suspend_security()
        current_employee = self.env.user.related_employee_id
        # if self.transfer_to_id.id == current_employee.id:
        #     raise ValidationError(_('Invalid Transfer To'))
        if is_task or model == 'workflow.task':
            task = task_obj.browse(active_id).filtered(lambda r: r.wkf_line_instance_state == 'processing')
            if not task:
                raise ValidationError(_('The current document approval status has been updated, please refresh.'))
        else:
            task = task_obj.search([('allocated_approver_id', '=', current_employee.id),
                                    ('approve_result_id', '=', None),
                                    ('wkf_line_instance_state', '=', 'processing'),
                                    ('wkf_line_instance_id.instance_id.approve_document', '=', model+','+str(active_id))])
            if not task:
                raise ValidationError(_('The current document approval status has been updated, please refresh.'))
        return result

    def action_linkup_extra_process(self, task_obj, model, active_id, current_employee):
        pass

    @api.multi
    def action_linkup(self):
        self = self.suspend_security()
        is_task = self.env.context.get('_is_task')
        model = self.env.context.get('active_model')
        active_id = self.env.context.get('active_id')
        task_obj = self.env['workflow.task'].suspend_security()
        current_employee = self.env.user.related_employee_id
        # if self.transfer_to_id.id == current_employee.id:
        #     return
        if is_task or model == 'workflow.task':
            task = task_obj.browse(active_id).filtered(lambda r: r.wkf_line_instance_state == 'processing')
            if not task:
                raise ValidationError(_('The current document approval status has been updated, please refresh.'))
            # trans_task = task_obj.create({'allocated_approver_id': self.transfer_to_id.id,
            #                               'wkf_line_instance_id': task.wkf_line_instance_id.id,
            #                               'authority_transfer': self.get_transfer_text(current_employee, self.transfer_to_id)})
            self.action_linkup_extra_process(task_obj, model, active_id, current_employee)
        else:
            task = task_obj.search([('allocated_approver_id', '=', current_employee.id),
                                    ('approve_result_id', '=', None),
                                    ('wkf_line_instance_state', '=', 'processing'),
                                    ('wkf_line_instance_id.instance_id.approve_document', '=', model+','+str(active_id))])
            if not task:
                raise ValidationError(_('The current document approval status has been updated, please refresh.'))
            else:
                task = task[0]
                self.action_linkup_extra_process(task_obj, model, active_id, current_employee)
            # trans_task = task_obj.search([('allocated_approver_id', '=', self.transfer_to_id.id),
            #                               ('approve_result_id', '=', None),
            #                               ('wkf_line_instance_state', '=', 'processing'),
            #                               ('wkf_line_instance_id.instance_id.approve_document', '=', model+','+str(active_id))])
            # if len(trans_task) == 0:
            #     trans_task = task_obj.create({'allocated_approver_id': self.transfer_to_id.id,
            #                                   'wkf_line_instance_id': task.wkf_line_instance_id.id,
            #                                   'authority_transfer': self.get_transfer_text(current_employee,
            #                                                                                self.transfer_to_id)})
        linkup_tasks = self.env['workflow.task']
        for transfer_to_id in self.transfer_to_ids:
            linkup_task = task_obj.create({'allocated_approver_id': transfer_to_id.id,
                                          'is_special_transfer': True, 'from_task_id': task.id,
                                           'authority_transfer': _('Linkup Nodes'),
                                          'wkf_line_instance_id': task.wkf_line_instance_id.id})
            linkup_tasks += linkup_task
        signal_id = self.env['workflow.signal'].search([('code', '=', 'linkup')], limit=1)
        # 审批节点没有配置转交信号，管理员仍可以强行转交
        task.approve_attachments_ids = self.attachment_ids
        task.notify_ids = self.notify_ids
        task.write({
            'actual_approver_id': current_employee.id,
            'signature_image': current_employee.signature_image,
            'approve_result_id': signal_id.id,
            'approve_note': self.comment,
            'approve_time': fields.Datetime.now(),
        })
        task.transfer_approve_attachments()
        task.with_context(prefetch_fields=False).wkf_line_instance_id.with_context(prefetch_fields=False).instance_id.with_context(prefetch_fields=False).approve_document.set_state_approving()
        linkup_tasks._wkf_send_email('need_approval_consult')
        if self.notify_ids:
            task.with_context({'partner_ids': self.notify_ids,'lang':self.env.context.get('lang','en_US')})._wkf_send_email('notify')
        setting = self.env.ref('workflow.workflow_default_setting', raise_if_not_found=False)
        if not setting.automatic_skip_after_approval:
            return
        return setting.after_process_task_return()
        # return_action = self.env.ref('workflow.action_workflow_task_to_be_approved')
        # act = return_action.read(['name', 'type', 'res_model', 'view_type', 'view_mode', 'target', 'domain', 'context'])[0]
        # if act.get('context') and act.get('context') != 'False':
        #     context = eval(act['context'])
        #     context['clear_breadcrumbs']= True
        #     context['search_disable_custom_filters']= False
        #     context['workflow_approving'] = True
        #     context = str(context).decode('gbk')
        #     act['context'] = context
        # else:
        #     act['context'] = ("{'clear_breadcrumbs':True,'workflow_approving':True}").decode('gbk')
        # act['domain'] = [('allocated_user_id', '=', self.env.user.id)]
        # return act
    # 移动端字段与功能
    mobile_operation = fields.Char()
    mobile_task_id = fields.Integer()

    @api.multi
    def mobile_linkup(self, task_id, comment, employees):
        self = self.suspend_security()
        ctx = {'active_model': 'workflow.task', '_is_task': True, 'active_id': task_id,
               'lang': self.env.context.get('lang', 'en_US')}
        record = self.with_context(ctx).create({'comment': comment or _("Linkup."),
                                                'transfer_to_ids': [(6, 0, employees.ids)]})
        task_obj = self.env['workflow.task'].suspend_security()
        task = task_obj.browse(task_id)
        try:
            record.sudo(task.allocated_user_id.id).with_context(ctx).action_linkup()
            result = (True, 'ok')
        except Exception as e:
            if hasattr(e, 'name'):
                error_msg = e.name
            elif hasattr(e, 'args'):
                error_msg = e.args[0]
            else:
                error_msg = str(e)
            result = (False, error_msg)
            self.env.cr.rollback()
        return result

    @api.model
    def process_boolean(self, string):
        if string == 'true':
            return True
        else:
            return False


class WorkflowAdd(models.TransientModel):
    _name = "workflow.add"
    _description = "Kthrp Base Workflow Add"

    # 转交至
    transfer_to_ids = fields.Many2many(comodel_name="hr.employee", string="Transfer To", required=True)
    # 意见
    comment = fields.Text(string="Note")
    # 通知
    notify_ids = fields.Many2many(comodel_name="hr.employee", string="Forward")
    # 附件
    attachment_ids = fields.Many2many(comodel_name='ir.attachment', string="Attachment")

    after_return = fields.Boolean(string='Return', default=False,
                                  help='If checked, you need to confirm after being approved by the person add to.')


    @api.model
    def default_get(self, fields):
        result = super(WorkflowAdd, self).default_get(fields)
        is_task = self.env.context.get('_is_task')
        model = self.env.context.get('active_model')
        active_id = self.env.context.get('active_id')
        task_obj = self.env['workflow.task'].suspend_security()
        current_employee = self.env.user.related_employee_id
        # if self.transfer_to_id.id == current_employee.id:
        #     raise ValidationError(_('Invalid Transfer To'))
        if is_task or model == 'workflow.task':
            task = task_obj.browse(active_id).filtered(lambda r: r.wkf_line_instance_state == 'processing')
            if not task:
                raise ValidationError(_('The current document approval status has been updated, please refresh.'))
        else:
            task = task_obj.search([('allocated_approver_id', '=', current_employee.id),
                                    ('approve_result_id', '=', None),
                                    ('wkf_line_instance_state', '=', 'processing'),
                                    ('wkf_line_instance_id.instance_id.approve_document', '=', model+','+str(active_id))])
            if not task:
                raise ValidationError(_('The current document approval status has been updated, please refresh.'))
        return result

    def action_add_extra_process(self, task_obj, model, active_id, current_employee):
        pass

    @api.multi
    def action_add(self):
        self = self.suspend_security()
        is_task = self.env.context.get('_is_task')
        model = self.env.context.get('active_model')
        active_id = self.env.context.get('active_id')
        task_obj = self.env['workflow.task'].suspend_security()
        current_employee = self.env.user.related_employee_id
        # if self.transfer_to_id.id == current_employee.id:
        #     return
        if is_task or model == 'workflow.task':
            task = task_obj.browse(active_id).filtered(lambda r: r.wkf_line_instance_state == 'processing')
            if not task:
                raise ValidationError(_('The current document approval status has been updated, please refresh.'))
            # trans_task = task_obj.create({'allocated_approver_id': self.transfer_to_id.id,
            #                               'wkf_line_instance_id': task.wkf_line_instance_id.id,
            #                               'authority_transfer': self.get_transfer_text(current_employee, self.transfer_to_id)})
            self.action_add_extra_process(task_obj, model, active_id, current_employee)
        else:
            task = task_obj.search([('allocated_approver_id', '=', current_employee.id),
                                    ('approve_result_id', '=', None),
                                    ('wkf_line_instance_state', '=', 'processing'),
                                    ('wkf_line_instance_id.instance_id.approve_document', '=', model+','+str(active_id))])
            if not task:
                raise ValidationError(_('The current document approval status has been updated, please refresh.'))
            else:
                task = task[0]
                self.action_add_extra_process(task_obj, model, active_id, current_employee)
            # trans_task = task_obj.search([('allocated_approver_id', '=', self.transfer_to_id.id),
            #                               ('approve_result_id', '=', None),
            #                               ('wkf_line_instance_state', '=', 'processing'),
            #                               ('wkf_line_instance_id.instance_id.approve_document', '=', model+','+str(active_id))])
            # if len(trans_task) == 0:
            #     trans_task = task_obj.create({'allocated_approver_id': self.transfer_to_id.id,
            #                                   'wkf_line_instance_id': task.wkf_line_instance_id.id,
            #                                   'authority_transfer': self.get_transfer_text(current_employee,
            #                                                                                self.transfer_to_id)})
        add_tasks = self.env['workflow.task']
        for transfer_to_id in self.transfer_to_ids:
            add_task = task_obj.create({'allocated_approver_id': transfer_to_id.id,
                                        'is_special_transfer': self.after_return, 'from_task_id': task.id,
                                        'authority_transfer': _('Add Nodes'),
                                        'wkf_line_instance_id': task.wkf_line_instance_id.id})
            add_tasks += add_task
        signal_id = self.env['workflow.signal'].search([('code', '=', 'add')], limit=1)
        # 审批节点没有配置转交信号，管理员仍可以强行转交
        task.approve_attachments_ids = self.attachment_ids
        task.notify_ids = self.notify_ids
        task.write({
            'actual_approver_id': current_employee.id,
            'signature_image': current_employee.signature_image,
            'approve_result_id': signal_id.id,
            'approve_note': self.comment,
            'approve_time': fields.Datetime.now(),
        })
        task.transfer_approve_attachments()
        task.with_context(prefetch_fields=False).wkf_line_instance_id.with_context(prefetch_fields=False).instance_id.with_context(prefetch_fields=False).approve_document.set_state_approving()
        add_tasks._wkf_send_email('need_approval')
        if self.notify_ids:
            task.with_context({'partner_ids': self.notify_ids,'lang':self.env.context.get('lang','en_US')})._wkf_send_email('notify')
        setting = self.env.ref('workflow.workflow_default_setting', raise_if_not_found=False)
        if not setting.automatic_skip_after_approval:
            return
        return setting.after_process_task_return()
        # return_action = self.env.ref('workflow.action_workflow_task_to_be_approved')
        # act = return_action.read(['name', 'type', 'res_model', 'view_type', 'view_mode', 'target', 'domain', 'context'])[0]
        # if act.get('context') and act.get('context') != 'False':
        #     context = eval(act['context'])
        #     context['clear_breadcrumbs']= True
        #     context['search_disable_custom_filters']= False
        #     context['workflow_approving'] = True
        #     context = str(context).decode('gbk')
        #     act['context'] = context
        # else:
        #     act['context'] = ("{'clear_breadcrumbs':True,'workflow_approving':True}").decode('gbk')
        # act['domain'] = [('allocated_user_id', '=', self.env.user.id)]
        # return act
    # 移动端字段与功能
    mobile_operation = fields.Char()
    mobile_task_id = fields.Integer()

    @api.multi
    def mobile_add(self, task_id, comment, employees, after_return=False):
        self = self.suspend_security()
        after_return = self.process_boolean(after_return)
        ctx = {'active_model': 'workflow.task', '_is_task': True, 'active_id': task_id,
               'lang': self.env.context.get('lang', 'en_US')}
        record = self.with_context(ctx).create({'comment': comment or _("Add."),
                                                'after_return': after_return,
                                                'transfer_to_ids': [(6, 0, employees.ids)]})
        task_obj = self.env['workflow.task'].suspend_security()
        task = task_obj.browse(task_id)
        try:
            record.sudo(task.allocated_user_id.id).with_context(ctx).action_add()
            result = (True, 'ok')
        except Exception as e:
            if hasattr(e, 'name'):
                error_msg = e.name
            elif hasattr(e, 'args'):
                error_msg = e.args[0]
            else:
                error_msg = str(e)
            result = (False, error_msg)
            self.env.cr.rollback()
        return result

    @api.model
    def process_boolean(self, string):
        if string == 'true':
            return True
        else:
            return False


class WorkflowWithdraw(models.TransientModel):
    _name = "workflow.withdraw"

    def action_confirm(self):
        self = self.suspend_security()
        self.env[self.env.context.get('active_model')].browse(self.env.context.get('active_id')).action_withdraw()


class WorkflowIntervene(models.TransientModel):
    _name = "workflow.intervene"
    _description = "Kthrp Base Workflow Intervene"

    # 目标节点
    target_node_id = fields.Many2one(comodel_name="workflow.line", string="Target Node",
                                     domain="[('id','in',node_ids[0][2])]", required=True)
    # 指定审批人
    approver_ids = fields.Many2many(comodel_name="hr.employee", string="Approver",
                                    relation="wkf_intervene_approver_hr_employee_rel", required=True)
    # 意见
    comment = fields.Text(string="Note")
    # 通知
    notify_ids = fields.Many2many(comodel_name="hr.employee", string="Notify")
    # 附件
    attachment_ids = fields.Many2many(comodel_name='ir.attachment', string="Attachment")

    @api.model
    def default_get(self, fields):
        result = super(WorkflowIntervene, self).default_get(fields)
        active_id = self.env.context.get('active_id')
        task_obj = self.env['workflow.task'].suspend_security()
        current_task = task_obj.browse(active_id).filtered(lambda r: r.wkf_line_instance_state == 'processing')
        if not current_task:
            raise ValidationError(_('The current document approval status has been updated, please refresh.'))
        return result

    @api.model
    def _default_nodes(self):
        # model = self.env.context.get('active_model')
        active_id = self.env.context.get('active_id')
        task_obj = self.env['workflow.task'].suspend_security()
        task = task_obj.browse(active_id)
        node = task.wkf_line_instance_id.workflow_line_id
        wkf = task.wkf_line_instance_id.instance_id.workflow_id
        return wkf.workflow_line_ids.filtered(lambda act: act.node_type == 'approve' and act.id != node.id)

    # 对应实例的节点
    node_ids = fields.Many2many(comodel_name="workflow.line", default=_default_nodes, compute="_compute_nodes")

    @api.one
    def _compute_nodes(self):
        model = self.env.context.get('active_model')
        active_id = self.env.context.get('active_id')
        task_obj = self.env['workflow.task'].suspend_security()
        task = task_obj.browse(active_id)
        node = task.wkf_line_instance_id.workflow_line_id
        wkf = task.wkf_line_instance_id.instance_id.workflow_id
        self.node_ids = wkf.workflow_line_ids.filtered(lambda act: act.node_type == 'approve' and act.id != node.id).ids

    # 干预操作
    @api.multi
    def action_intervene(self):
        active_id = self.env.context.get('active_id')
        next_node = self.target_node_id
        node_instance_obj = self.env['workflow.instance.line']
        task_obj = self.env['workflow.task'].suspend_security()
        current_task = task_obj.browse(active_id)
        if current_task.approve_result_id or current_task.wkf_line_instance_state != 'processing':
            raise ValidationError(_('The current document approval status has been updated, please refresh.'))
        current_task.with_context(prefetch_fields=False).wkf_line_instance_id.with_context(prefetch_fields=False).instance_id.with_context(prefetch_fields=False).approve_document.set_state_approving()
        current_task.wkf_line_instance_id.write({
            'state': 'processed',
            'process_time': fields.Datetime.now(),
        })
        # 添加
        tasks = []
        for approver in self.approver_ids:
            tasks.append((0, 0, {'allocated_approver_id': approver.id}))

        next_node_instance = node_instance_obj.create({'instance_id': current_task.wkf_line_instance_id.instance_id.id,
                                                       'workflow_line_id': next_node.id,
                                                       'task_ids': tasks})
        for task in next_node_instance.task_ids:
            task._wkf_send_email('need_approval')
        # 建立节点连接
        self.env['workflow.instance.line.transition'].create({
            'from_instance_line_id': current_task.wkf_line_instance_id.id,
            'to_instance_line_id': next_node_instance.id
        })
        next_node_instance.process()
        current_task.write({
            'actual_approver_id': self.env.user.related_employee_id.id,
            'signature_image': self.env.user.related_employee_id.signature_image,
            'approve_result_id': self.env['workflow.signal'].search([('code', '=', 'intervene')], limit=1).id,
            'approve_note': self.comment,
            'approve_time': fields.Datetime.now()
        })
        current_task.approve_attachments_ids = self.attachment_ids
        current_task.transfer_approve_attachments()
        current_task.notify_ids = self.notify_ids

        if self.notify_ids:
            next_node_instance.task_ids[0].notify_ids = self.notify_ids
            next_node_instance.task_ids[0].with_context({'partner_ids': self.notify_ids, 'lang':self.env.context.get('lang','en_US')})._wkf_send_email('notify')


class WorkflowBatchApprove(models.TransientModel):
    _name = 'workflow.batch.approve'
    _description = "Kthrp Base Workflow Batch Approve"

    # 意见
    comment = fields.Text(string="Note", default=lambda self: _("Agree."))
    # 通知
    notify_ids = fields.Many2many(comodel_name="hr.employee", string="Forward")
    # 附件
    attachment_ids = fields.Many2many(comodel_name='ir.attachment', string="Attachment")

    @api.multi
    def wizard_init(self):
        self = self.suspend_security()
        if self.env.context.get('leave_record_batch_approve', False):
            task_ids = []
            leave_records = self.env['kthrp.hr.leave.record'].browse(self.env.context.get('active_ids'))
            for rec in leave_records:
                task_ids.append(rec.task_ids[-1].id)
        else:
            task_ids = self.env.context.get('active_ids')
        task_obj = self.env['workflow.task'].suspend_security()
        tasks = task_obj.browse(task_ids).filtered(lambda l: l.wkf_line_instance_state != 'processing')
        if tasks:
            raise ValidationError(_('You cannot process completed approval task.'))
        view = self.env.ref('workflow.view_workflow_batch_approve_form')
        return {
            'name': _('Batch Approve'),
            'type': 'ir.actions.act_window',
            'view_type': 'form',
            'view_mode': 'form',
            'res_model': 'workflow.batch.approve',
            'views': [(view.id, 'form')],
            'view_id': view.id,
            'target': 'new',
            'res_id': self.id,
            'context': self.env.context,
        }

    @api.multi
    def wkf_batch_approve(self):
        self = self.suspend_security()
        if self.env.context.get('leave_record_batch_approve', False):
            task_ids = []
            leave_records = self.env['kthrp.hr.leave.record'].browse(self.env.context.get('active_ids'))
            for rec in leave_records:
                task_ids.append(rec.task_ids[-1].id)
        else:
            task_ids = self.env.context.get('active_ids')
        task_obj = self.env['workflow.task'].suspend_security()
        tasks = task_obj.search([('id', 'in', task_ids), ('wkf_line_instance_state', '=', 'processing')])
        # tasks = task_obj.browse(task_ids).filtered(lambda l: l.wkf_line_instance_state == 'processing')
        if not tasks:
            return
        notify_tasks = self.env['workflow.task']
        sql = '''SELECT
                    split_part(
                        kbwt.approve_document,
                        ',',
                        1
                    ) AS model,
                    model_instance_id,
                    kbwt. ID,
                (SELECT string_agg(kbws.code, ',') FROM workflow_signal kbws,
                  kthrp_wkf_node_signal kwns WHERE kwns.signal_id = kbws."id"
                AND kwns.node_id = kbwl."id") AS signals
                  
                FROM
                    workflow_task kbwt,
                  workflow_instance_line kbwil,
                  workflow_line kbwl
                WHERE kbwt.wkf_line_instance_id = kbwil."id"
                  AND kbwil.workflow_line_id = kbwl."id"
                  AND kbwt.id in %s'''
        tasks_dict  = {}
        self.env.cr.execute(sql, (tuple(tasks.ids),))
        values = self.env.cr.dictfetchall()
        for value in values:
            tasks_dict[value.get('id')] = [value.get('model') or '', value.get('model_instance_id') or False, value.get('signals')]
        error_flag = False
        error_msg = ''
        for task in tasks:
            self.env.cr.execute('SAVEPOINT batch_approve_task_%d' % task.id)
            try:
                infos = tasks_dict.get(task.id)
                document_model = infos[0]
                document_id = infos[1]
                signals = infos[2].split(',')
                next_node = task.get_next_node(document_id=document_id, document_model=document_model)
                if next_node:
                    if next_node.approver_type == 'designated_person':
                        raise ValidationError(_('Doc. %s need to designate an approver, you cannot execute the batch approve.') % task.document_name)
                if 'approve' in signals:
                    self.env[document_model].browse(document_id).set_state_approving()
                    task.action_approve()
                    # if not task.wkf_line_instance_id.is_end and (not task.is_finally_approve or task.is_special_transfer):
                    #     task._wkf_send_email('been_approved')
                    # elif task.is_finally_approve:
                    #     task._wkf_send_email('approved')
            except Exception as e:
                self.env.cr.execute('ROLLBACK TO SAVEPOINT batch_approve_task_%d' % task.id)
                error_flag = True
                if hasattr(e, 'name'):
                    e_msg = e.name
                elif hasattr(e, 'args'):
                    e_msg = e.args[0]
                else:
                    e_msg = str(e)
                error_msg += str(task.document_name) + ':' + e_msg + ';'
                continue
            notify_tasks += task
            # if not error_flag:
            #     self.env.cr.execute('SAVEPOINT x_batch_approve_task_%d' % task.id)
            #     x_error_flag = False
            #     try:
            #         self.env.cr.execute('RELEASE SAVEPOINT batch_approve_task_%d' % task.id)
            #     except Exception as e:
            #         self.env.cr.execute('ROLLBACK TO SAVEPOINT x_batch_approve_task_%d' % task.id)
            #         x_error_flag = True
            #     if not x_error_flag:
            #         self.env.cr.execute('RELEASE SAVEPOINT x_batch_approve_task_%d' % task.id)
        if notify_tasks:
            vals = {}
            if self.comment:
                # vals['approve_note'] = self.comment
                update_sql = '''update workflow_task set approve_note=%s WHERE id in %s'''
                self.env.cr.execute(update_sql, (self.comment, tuple(notify_tasks.ids),))
            if self.notify_ids:
                vals['notify_ids'] = [(4, nid) for nid in self.notify_ids.ids]
            if self.attachment_ids:
                vals['approve_attachments_ids'] = [(4, pid) for pid in self.attachment_ids.ids]
            if vals:
                notify_tasks.write(vals)
            if self.attachment_ids:
                for x in notify_tasks:
                    x.transfer_approve_attachments()
        if self.notify_ids and notify_tasks:
            notify_tasks.with_context({'partner_ids': self.notify_ids, 'lang':self.env.context.get('lang','en_US')})._wkf_send_email('notify')
        for task in notify_tasks:
            task.transition_notify()
        if error_flag:
            return (False, error_msg)
        else:
            return (True, 'ok')

    @api.multi
    def mobile_batch_approve(self, task_ids, comment):
        self = self.suspend_security()
        task_obj = self.env['workflow.task'].suspend_security()
        task = task_obj.browse(task_ids)
        ctx = {'active_model': 'workflow.task', '_is_task': True, 'active_ids': task_ids,
               'lang': self.env.context.get('lang', 'en_US')}

        record = self.with_context(ctx).create({'comment': comment or _("Agree.")})
        try:
            result = record.sudo(task[0].allocated_user_id.id).with_context(ctx).wkf_batch_approve()
        except Exception as e:
            if hasattr(e, 'name'):
                error_msg = e.name
            elif hasattr(e, 'args'):
                error_msg = e.args[0]
            else:
                error_msg = str(e)
            result = (False, error_msg)
            self.env.cr.rollback()
        return result


class WorkflowBatchReject(models.TransientModel):
    _name = 'workflow.batch.reject'
    _description = "Kthrp Base Workflow Batch Reject"

    # 意见
    comment = fields.Text(string="Note")
    # 通知
    notify_ids = fields.Many2many(comodel_name="hr.employee", string="Forward")
    # 附件
    attachment_ids = fields.Many2many(comodel_name='ir.attachment', string="Attachment")

    return_back = fields.Boolean(string='Skip Approved Nodes When Submitting', default=False)

    @api.multi
    def wizard_init(self):
        self = self.suspend_security()
        task_ids = self.env.context.get('active_ids')
        task_obj = self.env['workflow.task'].suspend_security()
        tasks = task_obj.browse(task_ids).filtered(lambda l: l.wkf_line_instance_state != 'processing')
        if tasks:
            raise ValidationError(_('You cannot process completed approval task.'))
        view = self.env.ref('workflow.view_workflow_batch_reject_form')
        return {
            'name': _('Batch Reject'),
            'type': 'ir.actions.act_window',
            'view_type': 'form',
            'view_mode': 'form',
            'res_model': 'workflow.batch.reject',
            'views': [(view.id, 'form')],
            'view_id': view.id,
            'target': 'new',
            'res_id': self.id,
            'context': self.env.context,
        }

    @api.multi
    def wkf_batch_reject(self):
        self = self.suspend_security()
        task_ids = self.env.context.get('active_ids')
        task_obj = self.env['workflow.task'].suspend_security()
        # tasks = task_obj.browse(task_ids).filtered(lambda l: l.wkf_line_instance_state == 'processing')
        tasks = task_obj.search([('id', 'in', task_ids), ('wkf_line_instance_state', '=', 'processing')])
        for rec in tasks:
            if self.return_back:
                rec.approve_document.is_wkf_return = True
            else:
                rec.approve_document.is_wkf_return = False
        sql = '''SELECT
                    kbwt."id",
                    "count" (*) AS COUNT
                FROM
                    workflow_task kbwt,
                    workflow_instance_line kbwil,
                    workflow_line kbwl,
                    workflow_signal kbws,
                    kthrp_wkf_node_signal kwns
                WHERE
                    kbwt.wkf_line_instance_id = kbwil."id"
                AND kbwil.workflow_line_id = kbwl."id"
                AND kwns.signal_id = kbws."id"
                AND kwns.node_id = kbwl."id"
                AND kbws.code = 'reject'
                AND kbwt."id" in %s
                GROUP BY
                    kbwt."id"'''
        tasks_dict  = {}
        self.env.cr.execute(sql, (tuple(tasks.ids),))
        values = self.env.cr.dictfetchall()
        for value in values:
            tasks_dict[value.get('id')] = value.get('count') or 0
        vals = {}
        if self.comment:
            # vals['approve_note'] = self.comment
            update_sql = '''update workflow_task set approve_note=%s WHERE id in %s'''
            self.env.cr.execute(update_sql, (self.comment,tuple(tasks.ids),))
        if self.notify_ids:
            vals['notify_ids'] = [(4, nid) for nid in self.notify_ids.ids]
        if self.attachment_ids:
            vals['approve_attachments_ids'] =  [(4, pid) for pid in self.attachment_ids.ids]
        if vals:
            tasks.write(vals)
        error_flag = False
        error_msg = ''
        for task in tasks:
            try:
                if tasks_dict.get(task.id):
                    task.action_refuse()
                    task._wkf_send_email('rejected')
            except Exception as e:
                error_flag = True
                if hasattr(e, 'name'):
                    error_msg = e.name
                elif hasattr(e, 'args'):
                    error_msg = e.args[0]
                else:
                    error_msg = str(e)
        if self.notify_ids:
            tasks.with_context({'partner_ids': self.notify_ids, 'lang':self.env.context.get('lang','en_US')})._wkf_send_email('notify')
        if error_flag:
            return (False, error_msg)
        else:
            return (True, 'ok')

    @api.model
    def process_boolean(self, string):
        if string == 'true':
            return True
        else:
            return False

    @api.multi
    def mobile_batch_reject(self, task_ids, comment, skip_approved_node):
        self = self.suspend_security()
        task_obj = self.env['workflow.task']
        task = task_obj.browse(task_ids)
        ctx = {'active_model': 'workflow.task', '_is_task': True, 'active_ids': task_ids,
               'lang': self.env.context.get('lang', 'en_US')}

        record = self.with_context(ctx).create({'comment': comment or _("Refuse."), 'return_back': self.process_boolean(skip_approved_node)})
        try:
            result = record.sudo(task[0].allocated_user_id.id).with_context(ctx).wkf_batch_reject()
        except Exception as e:
            if hasattr(e, 'name'):
                error_msg = e.name
            elif hasattr(e, 'args'):
                error_msg = e.args[0]
            else:
                error_msg = str(e)
            result = (False, error_msg)
            self.env.cr.rollback()
        return result


class WorkflowBatchTransfer(models.TransientModel):
    _name = 'workflow.batch.transfer'
    _description = "Kthrp Base Workflow Batch Transfer"

    transfer_to_id = fields.Many2one(comodel_name="hr.employee", string="Transfer To", required=True)
    # 意见
    comment = fields.Text(string="Note")
    # 通知
    notify_ids = fields.Many2many(comodel_name="hr.employee", string="Notify")
    # 附件
    attachment_ids = fields.Many2many(comodel_name='ir.attachment', string="Attachment")

    def get_default_after_return(self):
        setting = self.env.ref('workflow.workflow_default_setting', raise_if_not_found=False)
        if setting:
            return setting.default_of_return
        else:
            return True

    after_return = fields.Boolean(string='Return', default=get_default_after_return,
                                  help='If checked, you need to confirm after being approved by the person transferred to.')

    @api.multi
    def batch_transfer(self):
        self = self.suspend_security()
        active_ids = self.env.context.get('active_ids')
        task_obj = self.env['workflow.task'].suspend_security()
        current_employee = self.env.user.related_employee_id
        tasks = task_obj.browse(active_ids).filtered(lambda r: r.wkf_line_instance_state == 'processing')
        if len(active_ids) != len(tasks):
            raise ValidationError(_('Only workflow task’s state in processing can be transfered.'))
        if not tasks:
            raise ValidationError(_('The current document approval status has been updated, please refresh.'))
        for task in tasks:
            if self.transfer_to_id.id == task.allocated_approver_id.id or self.transfer_to_id.id == current_employee.id:
                continue
            trans_task = task_obj.create({'allocated_approver_id': self.transfer_to_id.id,
                                          'wkf_line_instance_id': task.wkf_line_instance_id.id,
                                          'authority_transfer': self.get_transfer_text(current_employee,
                                                                                         self.transfer_to_id)
            })
            if self.after_return:
                trans_task.write({'is_special_transfer': True, 'from_task_id': task.id})
            else:
                trans_task.write({'is_special_transfer': False, 'from_task_id': task.id})
            signal_id = self.env['workflow.signal'].search([('code', '=', 'transfer')], limit=1)
            task.approve_attachments_ids = self.attachment_ids
            task.notify_ids = self.notify_ids
            task.write({
                'actual_approver_id': current_employee.id,
                'signature_image': current_employee.signature_image,
                'approve_result_id': signal_id.id,
                'approve_note': self.comment,
                'approve_time': fields.Datetime.now(),
                'transfer_to_id': self.transfer_to_id.id

            })
            task.transfer_approve_attachments()
            task.with_context(prefetch_fields=False).wkf_line_instance_id.with_context(
                prefetch_fields=False).instance_id.with_context(
                prefetch_fields=False).approve_document.set_state_approving()
            if self.after_return:
                trans_task._wkf_send_email('need_approval_consult')
            else:
                trans_task._wkf_send_email('need_approval_transfer')
            if self.notify_ids:
                task.with_context({'partner_ids': self.notify_ids, 'lang': self.env.context.get('lang', 'en_US')})._wkf_send_email('notify')


    @api.multi
    def get_transfer_text(self, a, b):
        if not self.after_return:
            result = _('Transfer from %s to %s') % (a.name, b.name)
        else:
            result = _('%s consult from %s') % (a.name, b.name)
        return result


class WorkflowAllocate(models.TransientModel):
    _name = 'workflow.allocate'
    _description = "Kthrp Base Workflow Allocate"

    sequence = fields.Integer(string='Sequence', default=0)

    wkf_model_ids = fields.Many2many(comodel_name="ir.model", string="Model", required=True)

    use_min_sequence = fields.Boolean(string='Use Min Sequence', default=True)

    @api.multi
    def wizard_init(self):
        view = self.env.ref('workflow.view_workflow_allocate_form')
        return {
            'name': 'Allocate' if self.env.context.get('lang') == 'en_US' else u'分配',
            'type': 'ir.actions.act_window',
            'view_type': 'form',
            'view_mode': 'form',
            'res_model': 'workflow.allocate',
            'views': [(view.id, 'form')],
            'view_id': view.id,
            'target': 'new',
            'res_id': self.id,
            'context': self.env.context,
        }

    @api.multi
    @api.onchange('use_min_sequence')
    def _onchange_use_min_sequence(self):
        for rec in self:
            if rec.use_min_sequence:
                rec.sequence = False
        return self.wizard_init()

    @api.model
    def _default_wkf_models(self):
        wkf_models = []
        for rec in self.env['workflow.model'].search([]):
            wkf_models.append(rec.model_id.id)
        return self.env['ir.model'].search([('id', 'in', wkf_models)]).ids

    allow_wkf_model_ids = fields.Many2many(comodel_name='ir.model', compute='_compute_wkf_models',
                                           default=_default_wkf_models)

    @api.multi
    def _compute_wkf_models(self):
        for rec in self:
            wkf_models = []
            for record in self.env['workflow.model'].search([]):
                wkf_models.append(record.model_id.id)
            rec.allow_wkf_model_ids = wkf_models

    @api.multi
    def action_allocate(self):
        self = self.suspend_security()
        active_ids = self.env.context.get('active_ids')
        templates = self.env['workflow'].search([('id', 'in', active_ids), ('template', '=', True)])
        if not templates:
            raise ValidationError(_('No workflow template selected.'))
        new_workflows = self.env['workflow']
        for template in templates:
            if not self.wkf_model_ids:
                continue
            for wkf_model in self.wkf_model_ids:
                if wkf_model.id in template.allocation_ids.mapped('wkf_model_id').ids:
                    continue
                if self.use_min_sequence:
                    sql = '''SELECT COALESCE(MIN(sequence), 10) AS wkf_sequence  FROM workflow WHERE wkf_model_id=%s'''
                    self.env.cr.execute(sql, (wkf_model.id,))
                    wkf_sequence = self.env.cr.fetchone()[0] - 1
                else:
                    sql = '''SELECT count(*)AS wkf_count  FROM workflow WHERE wkf_model_id=%s and sequence=%s'''
                    self.env.cr.execute(sql, (wkf_model.id, self.sequence,))
                    wkf_count = self.env.cr.fetchone()[0]
                    if not wkf_count:
                        wkf_sequence = self.sequence
                    else:
                        wkf_sequence = self.sequence - 1
                if not wkf_sequence:
                    wkf_sequence -= 1
                new_workflow = template.copy({'sequence': wkf_sequence, 'wkf_model_id': wkf_model.id,
                                              'template': False, 'template_id': template.id,
                                              'name': template.name + '-' + wkf_model.name})
                new_workflows += new_workflow
        if new_workflows:
            domain = [('id', 'in', new_workflows.ids)]
            action = self.env.ref('workflow.action_workflow').read(
                ['name', 'type', 'res_model', 'view_type', 'view_mode', 'target', 'domain', 'context', 'view_id',
                 'search_view_id'])[0]
            action['domain'] = domain
            return action


class WorkflowAllocateCancel(models.TransientModel):
    _name = 'workflow.allocate.cancel'

    @api.multi
    def wizard_init(self):
        self = self.suspend_security()
        active_ids = self.env.context.get('active_ids')
        workflows = self.env['workflow'].search([('id', 'in', active_ids), ('template_id', '!=', False)])
        if not workflows:
            raise ValidationError(_('No workflow allocation selected.'))
        workflows.write({'template_id': False})
        return


class WorkflowCheck(models.TransientModel):
    _name = 'workflow.check'

    @api.multi
    def wizard_init(self):
        self = self.suspend_security()
        active_ids = self.env.context.get('active_ids')
        workflows = self.env['workflow'].browse(active_ids)
        out_errors = []
        in_errors = []
        error_msg = ''
        for workflow in workflows:
            for line in workflow.workflow_line_ids:
                if line.is_start:
                    if not line.out_transition_ids:
                        out_errors.append(line.name)
                elif line.is_end:
                    if not line.in_transition_ids:
                        in_errors.append(line.name)
                else:
                    if not line.out_transition_ids:
                        out_errors.append(line.name)
                    if not line.in_transition_ids:
                        in_errors.append(line.name)
        if out_errors:
            error_msg += _('Nodes 【%s】 do not maintain outgoing nodes. Check data.') % ','.join(out_errors)
        if in_errors:
            if error_msg:
                error_msg += '\n'
            error_msg += _('Nodes 【%s】 do not maintain inflow nodes. Check data.') % ','.join(in_errors)
        if error_msg:
            raise ValidationError(error_msg)
        else:
            if not self.env.context.get('success_no_msg'):
                raise ValidationError(_('Check passed.'))


class WorkflowTest(models.TransientModel):
    _name = 'workflow.test'

    doc_sel = fields.Char()

    doc = fields.Reference(selection='_select_objects', string="Test Document")

    submit_employee_id = fields.Many2one(comodel_name="hr.employee", string="Test Submit Employee")

    last_approver_id = fields.Many2one(comodel_name="hr.employee", string="Test Last Approver")

    ignore_condition_ids = fields.Many2many(comodel_name='workflow.condition', string='Ignore Condition')

    all_condition_ids = fields.Many2many(comodel_name='workflow.condition', relation='relation_all_condition_workflow_test')

    @api.model
    def _select_objects(self):
        if self.env.lang == 'zh_CN':
            sql = '''select im.model_name as model, COALESCE(it.value, im.name) as name 
    from workflow_model im
           left join ir_translation it on (it.name = 'ir.model,name' and it.lang = 'zh_CN' and res_id = im.model_id)'''
        else:
            sql = '''select model_name as model, name as name from workflow_model'''

        self._cr.execute(sql)
        sql_result = self._cr.dictfetchall()

        return [(record.get('model'), record.get('name')) for record in sql_result] + [('', '')]

    @api.multi
    def wizard_init(self):
        self = self.suspend_security()
        active_ids = self.env.context.get('active_ids')
        if len(active_ids) > 1:
            raise ValidationError(u'请逐条测试工作流！')
        workflow = self.env['workflow'].browse(active_ids)
        if workflow.template:
            raise ValidationError(u'工作流模板无法测试！')
        self.doc_sel = workflow.wkf_model_id.model
        conditions = self.env['workflow.condition']
        if workflow.wkf_condition_id:
            conditions += workflow.wkf_condition_id
        for line in workflow.workflow_line_ids:
            for x in line.out_transition_ids:
                if x.wkf_condition_id:
                    conditions += x.wkf_condition_id
        if conditions:
            self.all_condition_ids = conditions.ids
        self.env['workflow.check'].with_context(active_ids=active_ids,success_no_msg=True).create({}).wizard_init()
        view = self.env.ref('workflow.view_workflow_test_form')
        context = self.env.context.copy()
        return {
            'name': _(u'测试工作流'),
            'type': 'ir.actions.act_window',
            'view_type': 'form',
            'view_mode': 'form',
            'res_model': 'workflow.test',
            'views': [(view.id, 'form')],
            'view_id': view.id,
            'target': 'new',
            'res_id': self.id,
            'context': context,
        }

    @api.multi
    def action_test(self):
        self = self.suspend_security()
        active_ids = self.env.context.get('active_ids')
        if len(active_ids) > 1:
            raise ValidationError(u'请逐条测试工作流！')
        workflow = self.env['workflow'].browse(active_ids)
        if not self.doc:
            raise ValidationError(u'请选择测试单据！')
        test_msg = '开始测试工作流:【%s】\n' % workflow.name
        test_msg += '当前测试单据:【%s】\n' % self.doc.name_get()[0][1]
        if self.ignore_condition_ids:
            test_msg += '忽略条件:[%s]\n' % ','.join(self.ignore_condition_ids.mapped('name'))
        if self.submit_employee_id:
            test_msg += '单据提交人:[%s]\n' % self.submit_employee_id.name
        if workflow.wkf_condition_id and workflow.wkf_condition_id.id not in self.ignore_condition_ids.ids:
            h_result = workflow.wkf_condition_id.check_match_domain(self.doc._name, self.doc.id, extend_context={'submit_employee_id':self.submit_employee_id})
            if h_result:
                test_msg += '条件【%s】满足,可以测试' % workflow.wkf_condition_id.name
            else:
                test_msg += '条件【%s】不满足,终止测试' % workflow.wkf_condition_id.name
        elif workflow.wkf_condition_id and workflow.wkf_condition_id.id in self.ignore_condition_ids.ids:
            test_msg += '条件【%s】已设置忽略,测试继续' % workflow.wkf_condition_id.name

        wkf_lines = workflow.workflow_line_ids
        start_line = wkf_lines.filtered(lambda r: r.node_type == 'start')
        index = 0
        current_node = start_line
        finish_flag = False
        error_flag = False
        while current_node:
            index += 1
            if index >= 200:
                error_flag = True
                break
            if current_node.is_end:
                finish_flag = True
                break
            sequence = str(current_node.sequence)
            test_msg += '-' * 111 + '\n'
            test_msg += '%s==========>正在测试节点【%s】\n' % (sequence, current_node.name)
            approvors = self.get_approve_ids(current_node)
            if approvors:
                approvors = self.env['hr.employee'].with_context(prefetch_fields=False).browse(approvors)
                names = approvors.mapped('name')
                test_msg += '%s==========>节点【%s】审批人为【%s】\n' % (sequence, current_node.name,','.join(names))
            else:
                test_msg += '%s==========>节点【%s】没有找到审批人\n' % (sequence, current_node.name)

            transitions = current_node.out_transition_ids
            transitions = sorted(transitions, key=lambda x: x.sequence)
            current_success = False
            for transition in transitions:
                test_msg += '%s==========>流出节点【%s-%s】\n' % (sequence, str(transition.sequence), transition.to_workflow_line_id.name)
                if not transition.wkf_condition_id:
                    test_msg += '%s==========>无条件直接进入【%s】\n' % (sequence, transition.to_workflow_line_id.name)
                    current_node = transition.to_workflow_line_id
                    current_success = True
                    break
                elif transition.wkf_condition_id and transition.wkf_condition_id.id in self.ignore_condition_ids.ids:
                    test_msg += '%s==========>条件【%s】设置忽略，直接进入【%s】\n' % (sequence, transition.wkf_condition_id.name, transition.to_workflow_line_id.name)
                    current_node = transition.to_workflow_line_id
                    current_success = True
                    break
                elif transition.wkf_condition_id:
                    result = transition.wkf_condition_id.check_match_domain(self.doc._name, self.doc.id, extend_context={'submit_employee_id':self.submit_employee_id})
                    if result:
                        test_msg += '%s==========>条件【%s】满足，进入【%s】\n' % (sequence,
                        transition.wkf_condition_id.name, transition.to_workflow_line_id.name)
                        current_node = transition.to_workflow_line_id
                        current_success = True
                        break
                    else:
                        test_msg += '%s==========>条件【%s】不满足，测试下一个流出\n' % (sequence, transition.wkf_condition_id.name)
                else:
                    continue
            if not current_success:
                break
            test_msg += '%s==========>测试完成\n' % sequence
        test_msg += '-' * 111 + '\n'
        if error_flag:
            test_msg += '工作流测试循环超过200次请检查是否节点循环流出\n'
        if finish_flag:
            test_msg += '全部正常完成\n'
        if not finish_flag:
            test_msg += '异常，最后终止节点[%s]\n' % current_node.name
        raise ValidationError(test_msg)

    # 获取审批人
    @api.multi
    def get_approve_ids(self, next_node):
        self = self.suspend_security()
        records_obj = self.env['workflow.batch.transfer.records']
        approve_ids = []
        wkf_role_obj = self.env['workflow.role']
        if next_node.is_start or next_node.is_end:
            return approve_ids
        if next_node.approver_type == 'approve_role':
            for approver in wkf_role_obj.get_role_approver_test(self.doc, next_node):
                approve_ids.append(approver.id)
        elif next_node.approver_type == 'related_person':
            related_approver = None
            approver = None
            if next_node.approver_source == 'submitter':
                # 申请人
                related_approver = self.submit_employee_id or self.env.user.related_employee_id
            elif next_node.approver_source == 'last_approver':
                # 最后审批人
                related_approver = self.last_approver_id or self.env.user.related_employee_id
            elif next_node.approver_source == 'model_person':
                related_field = next_node.related_field.name
                object_model = self.doc
                if object_model:
                    related_approver = getattr(object_model, related_field)
            if related_approver:
                if next_node.related_person == 'manager':
                    approver = related_approver.manager_id
                elif next_node.related_person == 'department_head':
                    approver = related_approver.department_id.dept_manager_id
                elif next_node.related_person == 'parent_position':
                    position_id = related_approver.position_id
                    if position_id:
                        parent_position = position_id.superior_position_id
                        if parent_position:
                            approver = self.env['hr.employee'].search([('position_id', '=', parent_position.id)])
            if approver:
                approve_ids += approver.ids
        elif next_node.approver_type == 'designated_person':
            designated_ids = self.env.context.get('designated_approver', None)
            if designated_ids:
                approve_ids += designated_ids
            else:
                approve_ids += self.env.user.related_employee_id.ids
        elif next_node.approver_type == 'designated_position':
            position_employee = self.env['hr.employee'].search([('position_id', '=', next_node.position_id.id)])
            approve_ids += position_employee.ids
        # if current_node._name == 'workflow.instance.line':
        #     current_wkf_line = current_node.workflow_line_id
        # else:
        #     current_wkf_line = current_node
        if len(approve_ids) == 0:
            return approve_ids
        temp_approve_ids = []
        for approve_id in approve_ids:
            record = records_obj.get_transfer_record(next_node.workflow_id.wkf_model_id.id, approve_id)
            if record:
                temp_approve_ids += records_obj.transfer_employee_test(approve_id, record)
            else:
                temp_approve_ids += [approve_id]
        return temp_approve_ids


class WorkflowSubmitPrepare(models.TransientModel):
    _name = 'workflow.submit.prepare'

    submit_note = fields.Char(string='Submit Note')

    approver_id = fields.Many2one(comodel_name='hr.employee', string='Select Approver')

    display_submit_note = fields.Boolean()

    display_approver_id = fields.Boolean()

    submit_function_name = fields.Char()

    @api.model
    def wizard_init(self):
        self = self.suspend_security()
        model = self.env.context.get('workflow_model')
        active_id = self.env.context.get('x_active_id')
        submit_function_name = self.env.context.get('submit_function_name')
        workflow_interface_obj = self.env['workflow.interface']
        # 寻找审批流
        workflow = workflow_interface_obj.get_create_workflow_id(model, active_id)
        workflow = self.env['workflow'].browse(workflow)
        transitions = workflow.workflow_line_ids.filtered(lambda t: t.is_start).out_transition_ids
        # 选出符合条件的下一节点
        transitions = sorted(transitions, key=lambda x: x.sequence)
        next_transition = None
        for rec in transitions:
            if not rec.wkf_condition_id:
                next_transition = rec
                break
            else:
                if rec.wkf_condition_id.check_match_domain(model, active_id) is True:
                    next_transition = rec
                    break
                else:
                    pass
        if not next_transition:
            return False
        display_submit_note = False
        display_approver_id = False
        if next_transition.to_workflow_line_id.approver_type == 'dynamic_selection':
            display_approver_id = True
        if self.env['workflow.settings'].search([], limit=1).with_context(prefetch_fields=False).enable_again_submit_note:
            approve_document = self.env[model].browse(active_id)
            if approve_document.task_ids.filtered(lambda x: x.approve_result_id.code == 'reject'):
                display_submit_note = True
        if not display_submit_note and not display_approver_id:
            return False
        else:
            res = self.create({'display_submit_note': display_submit_note,
                               'display_approver_id': display_approver_id,
                               'submit_function_name': submit_function_name})
            view = self.env.ref('workflow.view_workflow_submit_prepare_form')
            context = self.env.context.copy()
            return {
                'name': _('Submit'),
                'type': 'ir.actions.act_window',
                'view_type': 'form',
                'view_mode': 'form',
                'res_model': self._name,
                'views': [(view.id, 'form')],
                'view_id': view.id,
                'target': 'new',
                'res_id': res.id,
                'context': context,
            }

    @api.multi
    def action_confirm(self):
        self = self.suspend_security()
        model = self.env.context.get('workflow_model')
        active_id = self.env.context.get('x_active_id')
        approve_document = self.env[model].browse(active_id)
        approve_document = approve_document.with_context(select_approver=self.approver_id.id,
                                                         submit_note=self.submit_note,
                                                         no_need_prepare=True)
        if hasattr(approve_document, self.submit_function_name):
            return getattr(approve_document, self.submit_function_name)()
        else:
            raise ValidationError(_(u'提交预处理参数与提交方法名不符，请联系管理员。'))